using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace SeaBattleControl
{
    public partial class SeaBattleMap : UserControl
    {
        //Length of the particular Grid
        private Int16 mGridSpan = 20; //px
        private const Int16 mGridLen = 11;

        //Arrays
        private System.Collections.ArrayList mBattleShipsArray = new System.Collections.ArrayList();
        private GridItem[,] mGridItemsArray = new GridItem[mGridLen, mGridLen];
        private String[] mXStrings = { "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
        private String[] mYStrings = { "", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J" };

        //Number of ships
        private int nNumOfCarriers,
                    nNumOfBattleships,
                    nNumOfDestroyers,
                    nNumOfGunboats;

        //Last position of selection sqare, and crosshair
        private Point mSelectionPoint, mCrosshairPoint;

        //SeaBattle Control mode
        private eSeaBattleControlMode mMode = eSeaBattleControlMode.Mode_NumberOfModes;

        //ID cntr
        private Int16 mIDCntr = 0;

        //Position validation flag
        private Boolean mPositionsValid = false;

        public Boolean PosStatus
        {
            get { return mPositionsValid; }
        }

        public eSeaBattleControlMode ControlMode
        {
            set { mMode = value; }
            get { return mMode; }
        }


        private enum eMaxNumOfShips
        {
            CARRIER = 1,
            BATTLESHIP = 2,
            DESTROYER = 2,
            GUNBOAT = 2
        };

        public enum eShipType
        {
            CARRIERType,
            BATTLESHIPType,
            DESTROYERType,
            GUNBOATType
        };

        public enum eSeaBattleControlMode
        {
            Mode_FireConsole,
            Mode_TacticMap,
            Mode_NumberOfModes
        };

        public enum ePosValidationResult
        {
            PosVal_Correct,
            PosVal_BaudsOverlapped,
            PosVal_ObjectCollision
        };

        private class GridItem
        {
            private Int16 mNumberOfHits = 0;
            private Int16 mItemsOnGrid = (Int16)eItemType.ItemType_Empty;

            public enum eItemType
            {
                ItemType_Empty = 0x0,
                ItemType_Hit = 0x1,
                ItemType_Selected = 0x2,
                ItemType_Cross = 0x4
            };

            public Int16 Hits
            {
                set { mNumberOfHits = value; }
                get { return mNumberOfHits; }
            }

            public Int16 Item
            {
                set { mItemsOnGrid = value; }
                get { return mItemsOnGrid; }
            }
        }

        public SeaBattleMap()
        {
            InitializeComponent();

            //Initialization of grid item list
            for (int x = 0; x < mGridLen; x++)
                for (int y = 0; y < mGridLen; y++)
                    mGridItemsArray[x, y] = new GridItem();
        }

        public void putShipsOnMap()
        {
            addWarShip(new Point(20, 20), eShipType.CARRIERType);
            addWarShip(new Point(40, 20), eShipType.BATTLESHIPType);
            addWarShip(new Point(60, 20), eShipType.BATTLESHIPType);
            addWarShip(new Point(80, 20), eShipType.DESTROYERType);
            addWarShip(new Point(100, 20), eShipType.DESTROYERType);
            addWarShip(new Point(120, 20), eShipType.GUNBOATType);
            addWarShip(new Point(140, 20), eShipType.GUNBOATType);
        }

        private void SeaBattle_Paint(object sender, PaintEventArgs e)
        {
            //Drawing grid
            drawGrid(e);

            //Dwawing warships
            drawWarShips(e);

            //Draw grid items
            if (mMode == eSeaBattleControlMode.Mode_FireConsole)
            {
                drawGridItems(e);
            }

            e.Dispose();
        }

        private void SeaBattle_MouseClick(object sender, MouseEventArgs e)
        {
            if (mMode == eSeaBattleControlMode.Mode_FireConsole)
            {
                //Remove previous selection
                mGridItemsArray[mCrosshairPoint.X / mGridSpan, mCrosshairPoint.Y / mGridSpan].Item &= ~((Int16)GridItem.eItemType.ItemType_Cross);

                //Save new point
                mCrosshairPoint.X = e.X - (e.X % mGridSpan);
                mCrosshairPoint.Y = e.Y - (e.Y % mGridSpan);

                //Set new grid item as selected
                mGridItemsArray[mCrosshairPoint.X / mGridSpan, mCrosshairPoint.Y / mGridSpan].Item |= (Int16)GridItem.eItemType.ItemType_Cross;

                this.Invalidate();
            }
        }

        private void SeaBattle_MouseMove(object sender, MouseEventArgs e)
        {
            if (mMode == eSeaBattleControlMode.Mode_FireConsole)
            {
                //Remove previous selection
                mGridItemsArray[mSelectionPoint.X / mGridSpan, mSelectionPoint.Y / mGridSpan].Item &= ~((Int16)GridItem.eItemType.ItemType_Selected);

                //Save new point
                mSelectionPoint.X = e.X - (e.X % mGridSpan);
                mSelectionPoint.Y = e.Y - (e.Y % mGridSpan);

                //Set new grid item as selected
                mGridItemsArray[mSelectionPoint.X / mGridSpan, mSelectionPoint.Y / mGridSpan].Item |= (Int16)GridItem.eItemType.ItemType_Selected;

                this.Invalidate();
            }
        }

        private void SeaBattle_DragEnter(object sender, DragEventArgs e)
        {
            System.Console.WriteLine("SeaBattle_DragEnter data: {0}", e.Data.ToString());
            e.Effect = DragDropEffects.Move;
        }

        private void SeaBattle_DragDrop(object sender, DragEventArgs e)
        {
            System.Console.WriteLine("SeaBattle_DragDrop x={0} y={1} e.Data={2}",
                e.X, e.Y, e.Data.ToString());

            validateAllPositions();
        }

        private void drawGrid(PaintEventArgs e)
        {
            Pen pen = new Pen(Color.LightGray, 1);
            Pen blackPen = new Pen(Color.Black, 2);
            Font f = new Font(FontFamily.GenericMonospace, 15, FontStyle.Bold);
            Brush b = new SolidBrush(Color.LightGray);
            Int16 cntr = 0;

            //Vertical lines
            for (int i = 0; i < this.Width; i += mGridSpan)
            {
                Point p1 = new Point(i, mGridSpan);
                Point p2 = new Point(i, this.Height);

                if (cntr == 0)
                {
                    e.Graphics.DrawString(mXStrings[cntr], f, b, new Point(i + 1, 0));
                    cntr++;
                }
                else
                {
                    e.Graphics.DrawString(mXStrings[cntr], f, b, new Point(i + 1, 0));
                    e.Graphics.DrawLine(pen, p1, p2);
                    cntr++;
                }

            }

            cntr = 0;

            //Horizontal lines
            for (int i = 0; i < this.Height; i += mGridSpan)
            {
                Point p1 = new Point(mGridSpan, i);
                Point p2 = new Point(this.Width, i);

                if (cntr == 0)
                {
                    e.Graphics.DrawString(mYStrings[cntr], f, b, new Point(0, i));
                    cntr++;
                }
                else
                {
                    e.Graphics.DrawString(mYStrings[cntr], f, b, new Point(0, i));
                    e.Graphics.DrawLine(pen, p1, p2);
                    cntr++;
                }
            }
        }

        private void drawWarShips(PaintEventArgs e)
        {
            if (mBattleShipsArray.Count > 0)
            {
                foreach (BattleshipSprite bShip in mBattleShipsArray)
                {
                    this.Controls.Add(bShip);
                    bShip.Show();
                }
            }
        }

        private void drawGridItems(PaintEventArgs e)
        {
            for (int x = 1; x < mGridLen; x++)
            {
                for (int y = 1; y < mGridLen; y++)
                {
                    if ((mGridItemsArray[x, y].Item & (Int16)GridItem.eItemType.ItemType_Hit) != 0)
                    {
                        Font f = new Font(FontFamily.GenericMonospace, 15, FontStyle.Bold);

                        if (mGridItemsArray[x, y].Hits == 0)
                        {
                            e.Graphics.DrawString(".", f, new SolidBrush(Color.Black), new Point(x * mGridSpan, y * mGridSpan));
                        }
                        else
                        {
                            e.Graphics.DrawString(mGridItemsArray[x, y].Hits.ToString(), f, new SolidBrush(Color.Black), new Point(x * mGridSpan, y * mGridSpan));
                        }
                    }

                    if ((mGridItemsArray[x, y].Item & (Int16)GridItem.eItemType.ItemType_Cross) != 0)
                    {
                        Pen crosspen = new Pen(Color.Red, 3);

                        e.Graphics.DrawLine(crosspen, mCrosshairPoint, new Point(mCrosshairPoint.X + mGridSpan, mCrosshairPoint.Y + mGridSpan));
                        e.Graphics.DrawLine(crosspen, new Point(mCrosshairPoint.X + mGridSpan, mCrosshairPoint.Y),
                                                 new Point(mCrosshairPoint.X, mCrosshairPoint.Y + mGridSpan));
                    }

                    if ((mGridItemsArray[x, y].Item & (Int16)GridItem.eItemType.ItemType_Selected) != 0)
                    {
                        Pen pen = new Pen(Color.Red, 3);
                        e.Graphics.DrawRectangle(pen, mSelectionPoint.X, mSelectionPoint.Y, mGridSpan, mGridSpan);
                    }
                }
            }
        }

        public bool addWarShip(Point position, eShipType type)
        {
            bool fRetVal = false;

            //Increment ID cntr;
            mIDCntr++;

            switch (type)
            {
                case eShipType.CARRIERType:
                    {
                        if (nNumOfCarriers < (int)eMaxNumOfShips.CARRIER)
                        {
                            BattleshipSprite battleShip = new BattleshipSprite(mIDCntr, mGridSpan, this);
                            battleShip.BackColor = System.Drawing.Color.Black;
                            battleShip.Bounds = new Rectangle(position, new Size(mGridSpan, mGridSpan * 4));

                            mBattleShipsArray.Add(battleShip);
                            nNumOfCarriers++;
                            fRetVal = true;
                        }
                        break;
                    }
                case eShipType.BATTLESHIPType:
                    {
                        if (nNumOfBattleships < (int)eMaxNumOfShips.BATTLESHIP)
                        {
                            BattleshipSprite battleShip = new BattleshipSprite(mIDCntr, mGridSpan, this);
                            battleShip.BackColor = System.Drawing.Color.Black;
                            battleShip.Bounds = new Rectangle(position, new Size(mGridSpan, mGridSpan * 3));

                            mBattleShipsArray.Add(battleShip);
                            nNumOfBattleships++;
                            fRetVal = true;
                        }
                        break;
                    }
                case eShipType.DESTROYERType:
                    {
                        if (nNumOfDestroyers < (int)eMaxNumOfShips.DESTROYER)
                        {
                            BattleshipSprite battleShip = new BattleshipSprite(mIDCntr, mGridSpan, this);
                            battleShip.BackColor = System.Drawing.Color.Black;
                            battleShip.Bounds = new Rectangle(position, new Size(mGridSpan, mGridSpan * 2));

                            mBattleShipsArray.Add(battleShip);
                            nNumOfDestroyers++;
                            fRetVal = true;
                        }
                        break;
                    }
                case eShipType.GUNBOATType:
                    {
                        if (nNumOfGunboats < (int)eMaxNumOfShips.GUNBOAT)
                        {
                            BattleshipSprite battleShip = new BattleshipSprite(mIDCntr, mGridSpan, this);
                            battleShip.BackColor = System.Drawing.Color.Black;
                            battleShip.Bounds = new Rectangle(position, new Size(mGridSpan, mGridSpan));

                            mBattleShipsArray.Add(battleShip);
                            nNumOfGunboats++;
                            fRetVal = true;
                        }
                        break;
                    }
                default:
                    {
                        break;
                    }
            }

            return fRetVal;
        }

        private void validateAllPositions()
        {
            Boolean fConflict = false;
            foreach (BattleshipSprite bShip1 in mBattleShipsArray)
            {
                foreach (BattleshipSprite bShip2 in mBattleShipsArray)
                {
                    if (bShip1.ID != bShip2.ID)
                    {
                        fConflict = false;

                        for (int y = bShip1.Bounds.Y; y <= bShip1.Bounds.Y + bShip1.Bounds.Height; y += mGridSpan)
                        {
                            for (int x = bShip1.Bounds.X; x <= bShip1.Bounds.X + bShip1.Bounds.Width; x += mGridSpan)
                            {
                                if (x == bShip2.Bounds.X && y == bShip2.Bounds.Y)
                                {
                                    fConflict = true;                                   
                                }
                            }
                        }

                        if (fConflict)
                        {
                            bShip1.BackColor = Color.Red;
                            bShip2.BackColor = Color.Red;

                            Console.WriteLine("pos conflict {0} - {1}", bShip1.ID, bShip2.ID);
                        }
                        else
                        {
                            bShip1.BackColor = Color.Black;
                            bShip2.BackColor = Color.Black;

                            Console.WriteLine("pos not conflict {0} - {1}", bShip1.ID, bShip2.ID);
                        }
                    }
                }
            }
        }

        public ePosValidationResult validatePosition(Int16 id, int x, int y, int w, int h)
        {
            ePosValidationResult result = ePosValidationResult.PosVal_Correct;

            //Test against map bounds
            if ((x < mGridSpan) || ((x + w) > Width) || (y < mGridSpan) || ((y + h) > Height))
            {
                result = ePosValidationResult.PosVal_BaudsOverlapped;
            }
            //else
            //{
            //    //Test against positions of other objects
            //    foreach (BattleshipSprite bShip in mBattleShipsArray)
            //    {
            //        if (bShip.ID != id)
            //        {
            //            Rectangle bShipBounds = bShip.Bounds;

            //            if ((bShipBounds.X == x) || ((bShipBounds.X + bShipBounds.Width) == x) ||
            //                (bShipBounds.X == (x + w)) || ((bShipBounds.X + bShipBounds.Width) == (x + w)))
            //            {
            //                if ((bShipBounds.Y == y) || ((bShipBounds.Y + bShipBounds.Height) == y))
            //                {
            //                    result = ePosValidationResult.PosVal_ObjectCollision;
            //                    Console.WriteLine("a");
            //                    break;
            //                }

            //                if ((bShipBounds.Y == (y + h)) || (bShipBounds.Y + bShipBounds.Height) == (y + h))
            //                {
            //                    result = ePosValidationResult.PosVal_ObjectCollision;
            //                    Console.WriteLine("b");
            //                    break;
            //                }

            //                if ((y > bShipBounds.Y) && (y <= (bShipBounds.Y + bShipBounds.Height)))
            //                {
            //                    result = ePosValidationResult.PosVal_ObjectCollision;
            //                    Console.WriteLine("c");
            //                    break;
            //                }
            //            }

            //            if ((bShipBounds.Y == y) || ((bShipBounds.Y + bShipBounds.Height) == y) ||
            //                (bShipBounds.Y == (y + h)) || ((bShipBounds.Y + bShipBounds.Height) == (y + h)))
            //            {
            //                if ((bShipBounds.X == x) || ((bShipBounds.X + bShipBounds.Width) == x))
            //                {
            //                    result = ePosValidationResult.PosVal_ObjectCollision;
            //                    Console.WriteLine("d");
            //                    break;
            //                }

            //                if ((bShipBounds.X == (x + w)) || (bShipBounds.X + bShipBounds.Width) == (x + w))
            //                {
            //                    result = ePosValidationResult.PosVal_ObjectCollision;
            //                    Console.WriteLine("e");
            //                    break;
            //                }

            //                if ((x > bShipBounds.X) && (x <= (bShipBounds.X + bShipBounds.Width)))
            //                {
            //                    result = ePosValidationResult.PosVal_ObjectCollision;
            //                    Console.WriteLine("f");
            //                    break;
            //                }
            //            }
            //        }
            //    }
            //}

            if (result != ePosValidationResult.PosVal_Correct)
            {
                mPositionsValid = false;
            }
            else
            {
                mPositionsValid = true;
            }

            return result;
        }

        public Point getSelectedSquare()
        {
            return new Point(mCrosshairPoint.X / mGridSpan, mCrosshairPoint.Y / mGridSpan);
        }

        public Boolean updateHits(Point p, Int16 numOfHits)
        {
            Boolean retVal = false;
            if ((p.X > 0) && (p.X < mGridLen) && (p.Y > 0) && (p.Y < mGridLen))
            {
                if (numOfHits > 0)
                {
                    mGridItemsArray[p.X, p.Y].Item |= (Int16)GridItem.eItemType.ItemType_Hit;
                    mGridItemsArray[p.X, p.Y].Hits = numOfHits;
                }
                else
                {
                    mGridItemsArray[p.X, p.Y].Item |= (Int16)GridItem.eItemType.ItemType_Hit;
                    mGridItemsArray[p.X, p.Y].Hits = 0;
                }

                //update grid
                this.Invalidate();

                retVal = true;
            }

            return retVal;
        }
    }
}
